package utils

import (
	"blog_go/global"
	"encoding/json"
	"errors"
	"github.com/dgrijalva/jwt-go/v4"
	"github.com/sirupsen/logrus"
	"time"
)

// JWTPayLoad jwt中payload数据
type JwtPayLoad struct {
	NickName string `json:"nick_name"`
	Avatar   string `json:"avatar"` // 头像
	Role     int    `json:"role"`   // 1管理员 2普通用户 3游客
	UserID   uint   `json:"user_id"`
}

func (j JwtPayLoad) MarshalBinary() ([]byte, error) {
	return json.Marshal(j)
}

type CustomClaims struct {
	JwtPayLoad
	jwt.StandardClaims
}

// 获得token
func GetToken(user JwtPayLoad) (tokenString string, err error) {
	var mySigningKey = []byte(global.Conf.Jwt.Secret)
	claim := CustomClaims{
		JwtPayLoad: user,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: jwt.At(time.Now().Add(time.Hour * time.Duration(global.Conf.Jwt.Expire))),
			Issuer:    global.Conf.Jwt.Issuer,
		},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claim)
	return token.SignedString(mySigningKey)
}

// 解析token
func ParseToken(tokenStr string) (*CustomClaims, error) {
	var mySigningKey = []byte(global.Conf.Jwt.Secret)
	token, err := jwt.ParseWithClaims(tokenStr, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte(mySigningKey), nil
	})
	if err != nil {
		logrus.Error("token解析错误:", err)
		goto ERR
	}

	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		return claims, nil
	}
ERR:
	return nil, errors.New("token校验错误")
}

// 获取token在redis的key
func getTokenRedisKey(token string) string {
	return global.Conf.Name + "_" + token
}

// 设置token缓存
func SetTokenRedis(token string, jwtInfo JwtPayLoad) {
	redisKey := getTokenRedisKey(token)
	global.Redis.Set(redisKey, jwtInfo, time.Duration(global.Conf.Jwt.Expire)*time.Hour)
}

// 获取token的info
func GetInfoByToken(token string) JwtPayLoad {
	redisKey := getTokenRedisKey(token)
	result, _ := global.Redis.Get(redisKey).Result()
	var jwtInfo JwtPayLoad
	err := json.Unmarshal([]byte(result), &jwtInfo)
	if err != nil {
		logrus.Error(err)
		panic(err)
	}
	return jwtInfo
}

// 注销登录
func LogoutToken(token string, claims *CustomClaims) {
	redisKey := getTokenRedisKey(token) + "_" + "logout"
	at := claims.ExpiresAt
	diff := at.Sub(time.Now())
	global.Redis.Set(redisKey, "", diff)
}

// 是否注销
func LogoutTokenExists(token string) bool {
	redisKey := getTokenRedisKey(token) + "_" + "logout"
	result, _ := global.Redis.Exists(redisKey).Result()
	if result > 0 {
		return true
	}
	return false
}
